{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "b651ead9-5504-45ee-938d-f91ac78dddd1",
      "metadata": {
        "id": "b651ead9-5504-45ee-938d-f91ac78dddd1"
      },
      "source": [
        "# Chatbot with message summarization\n",
        "\n",
        "## Review\n",
        "\n",
        "We've covered how to customize graph state schema and reducer.\n",
        "\n",
        "We've also shown a number of ways to trim or filter messages in graph state.\n",
        "\n",
        "## Goals\n",
        "\n",
        "Now, let's take it one step further!\n",
        "\n",
        "Rather than just trimming or filtering messages, we'll show how to use LLMs to produce a running summary of the conversation.\n",
        "\n",
        "This allows us to retain a compressed representation of the full conversation, rather than just removing it with trimming or filtering.\n",
        "\n",
        "We'll incorporate this summarization into a simple Chatbot.  \n",
        "\n",
        "And we'll equip that Chatbot with memory, supporting long-running conversations without incurring high token cost / latency."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "000a6daa-92ad-4e57-a060-d1c81176eb0d",
      "metadata": {
        "id": "000a6daa-92ad-4e57-a060-d1c81176eb0d"
      },
      "outputs": [],
      "source": [
        "%%capture --no-stderr\n",
        "%pip install --quiet -U langchain_core langgraph langchain_google_genai"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "e39c0535",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "e39c0535",
        "outputId": "3fbc1402-b227-4464-9e17-7bb262abb62b"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "env: GOOGLE_API_KEY=AIzaSyCD_du_wlnoyfrBSnS9k-fyqcs-C2VKqI4\n",
            "AIzaSyCD_du_wlnoyfrBSnS9k-fyqcs-C2VKqI4\n"
          ]
        }
      ],
      "source": [
        "from google.colab import userdata\n",
        "\n",
        "%env GOOGLE_API_KEY = {userdata.get('GEMINI_API_KEY')}\n",
        "\n",
        "import os\n",
        "print(os.environ[\"GOOGLE_API_KEY\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dfddfce9-3a9b-4b35-a76d-28265515aabd",
      "metadata": {
        "id": "dfddfce9-3a9b-4b35-a76d-28265515aabd"
      },
      "source": [
        "We'll use [LangSmith](https://docs.smith.langchain.com/) for [tracing](https://docs.smith.langchain.com/concepts/tracing)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "464856d4",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "464856d4",
        "outputId": "bb258530-a346-46af-8559-d5ebd6cb8e21"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "env: LANGCHAIN_API_KEY=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\n"
          ]
        }
      ],
      "source": [
        "%env LANGCHAIN_API_KEY = {userdata.get('LANGCHAIN_API_KEY')}\n",
        "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
        "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-academy\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "6544ba0f",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "6544ba0f",
        "outputId": "58363226-092a-4afd-bcf3-0561e5758d92"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "AIMessage(content='Hi there! How can I help you today? \\n', additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': [{'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-208fe213-10ef-45d9-a8d9-8ee017347326-0', usage_metadata={'input_tokens': 2, 'output_tokens': 11, 'total_tokens': 13})"
            ]
          },
          "metadata": {},
          "execution_count": 11
        }
      ],
      "source": [
        "from langchain_google_genai import ChatGoogleGenerativeAI\n",
        "model: ChatGoogleGenerativeAI = ChatGoogleGenerativeAI(model = \"gemini-1.5-flash\")\n",
        "model.invoke(\"hi\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "db3afac3-8b7a-45db-a3c1-7e4125c1bc8b",
      "metadata": {
        "id": "db3afac3-8b7a-45db-a3c1-7e4125c1bc8b"
      },
      "source": [
        "We'll use `MessagesState`, as before.\n",
        "\n",
        "In addition to the built-in `messages` key, we'll now include a custom key (`summary`)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "948e60f0-5c76-4235-b40e-cf523205d40e",
      "metadata": {
        "id": "948e60f0-5c76-4235-b40e-cf523205d40e"
      },
      "outputs": [],
      "source": [
        "from langgraph.graph import MessagesState\n",
        "\n",
        "class State(MessagesState):\n",
        "    summary: str"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6855ea31-5cc1-4277-a189-0b72459f67ec",
      "metadata": {
        "id": "6855ea31-5cc1-4277-a189-0b72459f67ec"
      },
      "source": [
        "We'll define a node to call our LLM that incorporates a summary, if it exists, into the prompt."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "c3f7d19b-afe0-4381-9b1a-0a832b162e7b",
      "metadata": {
        "id": "c3f7d19b-afe0-4381-9b1a-0a832b162e7b"
      },
      "outputs": [],
      "source": [
        "from langchain_core.messages import SystemMessage, HumanMessage, RemoveMessage\n",
        "\n",
        "# Define the logic to call the model\n",
        "def call_model(state: State) -> State:\n",
        "\n",
        "    # Get summary if it exists\n",
        "    summary = state.get(\"summary\", \"\")\n",
        "\n",
        "    # If there is summary, then we add it\n",
        "    if summary:\n",
        "\n",
        "        # Add summary to system message\n",
        "        system_message = f\"Summary of conversation earlier: {summary}\"\n",
        "\n",
        "        # Append summary to any newer messages\n",
        "        messages = [SystemMessage(content=system_message)] + state[\"messages\"]\n",
        "\n",
        "    else:\n",
        "        messages = state[\"messages\"]\n",
        "\n",
        "    response = model.invoke(messages)\n",
        "    return {\"messages\": response}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6882042c-b42d-4d52-a6a7-6ec8efa72450",
      "metadata": {
        "id": "6882042c-b42d-4d52-a6a7-6ec8efa72450"
      },
      "source": [
        "We'll define a node to produce a summary.\n",
        "\n",
        "Note, here we'll use `RemoveMessage` to filter our state after we've produced the summary."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "78c7aa59-3760-4e76-93f1-bc713e3ec39e",
      "metadata": {
        "id": "78c7aa59-3760-4e76-93f1-bc713e3ec39e"
      },
      "outputs": [],
      "source": [
        "def summarize_conversation(state: State):\n",
        "\n",
        "    # First, we get any existing summary\n",
        "    summary = state.get(\"summary\", \"\")\n",
        "\n",
        "    # Create our summarization prompt\n",
        "    if summary:\n",
        "\n",
        "        # A summary already exists\n",
        "        summary_message = (\n",
        "            f\"This is summary of the conversation to date: {summary}\\n\\n\"\n",
        "            \"Extend the summary by taking into account the new messages above:\"\n",
        "        )\n",
        "\n",
        "    else:\n",
        "        summary_message = \"Create a summary of the conversation above:\"\n",
        "\n",
        "    # Add prompt to our history\n",
        "    messages = state[\"messages\"] + [HumanMessage(content=summary_message)]\n",
        "    response = model.invoke(messages)\n",
        "\n",
        "    # Delete all but the 2 most recent messages\n",
        "    delete_messages = [RemoveMessage(id=m.id) for m in state[\"messages\"][:-2]]\n",
        "    return {\"summary\": response.content, \"messages\": delete_messages}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f982993e-f4be-4ff7-9a38-886f75398b3d",
      "metadata": {
        "id": "f982993e-f4be-4ff7-9a38-886f75398b3d"
      },
      "source": [
        "We'll add a conditional edge to determine whether to produce a summary based on the conversation length."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "b507665d-7f5d-442a-b498-218c94c5dd8b",
      "metadata": {
        "id": "b507665d-7f5d-442a-b498-218c94c5dd8b"
      },
      "outputs": [],
      "source": [
        "from langgraph.graph import END\n",
        "# Determine whether to end or summarize the conversation\n",
        "def should_continue(state: State):\n",
        "\n",
        "    \"\"\"Return the next node to execute.\"\"\"\n",
        "\n",
        "    messages = state[\"messages\"]\n",
        "\n",
        "    # If there are more than six messages, then we summarize the conversation\n",
        "    if len(messages) > 6:\n",
        "        return \"summarize_conversation\"\n",
        "\n",
        "    # Otherwise we can just end\n",
        "    return END"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a838f4c-7067-4f7f-a4c4-6654e11214cd",
      "metadata": {
        "id": "5a838f4c-7067-4f7f-a4c4-6654e11214cd"
      },
      "source": [
        "## Adding memory\n",
        "\n",
        "Recall that [state is transient](https://github.com/langchain-ai/langgraph/discussions/352#discussioncomment-9291220) to a single graph execution.\n",
        "\n",
        "This limits our ability to have multi-turn conversations with interruptions.\n",
        "\n",
        "As introduced at the end of Module 1, we can use [persistence](https://langchain-ai.github.io/langgraph/how-tos/persistence/) to address this!\n",
        "\n",
        "LangGraph can use a checkpointer to automatically save the graph state after each step.\n",
        "\n",
        "This built-in persistence layer gives us memory, allowing LangGraph to pick up from the last state update.\n",
        "\n",
        "As we previously showed, one of the easiest to work with is `MemorySaver`, an in-memory key-value store for Graph state.\n",
        "\n",
        "All we need to do is compile the graph with a checkpointer, and our graph has memory!"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "1d57516d-f9f1-4d3c-a84a-7277b5ce6df6",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 350
        },
        "id": "1d57516d-f9f1-4d3c-a84a-7277b5ce6df6",
        "outputId": "ab011476-6f29-4c63-f375-97ace5866f02"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/jpeg": "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\n",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {}
        }
      ],
      "source": [
        "from IPython.display import Image, display\n",
        "from langgraph.checkpoint.memory import MemorySaver\n",
        "from langgraph.graph import StateGraph, START\n",
        "from langgraph.graph.state import CompiledStateGraph\n",
        "\n",
        "# Define a new graph\n",
        "workflow: StateGraph = StateGraph(State)\n",
        "workflow.add_node(\"conversation\", call_model)\n",
        "workflow.add_node(summarize_conversation)\n",
        "\n",
        "# Set the entrypoint as conversation\n",
        "workflow.add_edge(START, \"conversation\")\n",
        "workflow.add_conditional_edges(\"conversation\", should_continue)\n",
        "workflow.add_edge(\"summarize_conversation\", END)\n",
        "\n",
        "# Compile\n",
        "memory: MemorySaver = MemorySaver()\n",
        "graph: CompiledStateGraph = workflow.compile(checkpointer=memory)\n",
        "display(Image(graph.get_graph().draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d0bd5d23-ac3b-4496-a049-9a9f97d2feb9",
      "metadata": {
        "id": "d0bd5d23-ac3b-4496-a049-9a9f97d2feb9"
      },
      "source": [
        "## Threads\n",
        "\n",
        "The checkpointer saves the state at each step as a checkpoint.\n",
        "\n",
        "These saved checkpoints can be grouped into a `thread` of conversation.\n",
        "\n",
        "Think about Slack as an analog: different channels carry different conversations.\n",
        "\n",
        "Threads are like Slack channels, capturing grouped collections of state (e.g., conversation).\n",
        "\n",
        "Below, we use `configurable` to set a thread ID.\n",
        "\n",
        "![state.jpg](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbadf3b379c2ee621adfd1_chatbot-summarization1.png)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "2566c93b-13e6-4a53-bc0f-b00fff691d30",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "2566c93b-13e6-4a53-bc0f-b00fff691d30",
        "outputId": "b4d59a58-886e-4567-be2b-b9024be83fbd"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "Hi Lance! Nice to meet you. What can I do for you today?\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "You told me your name is Lance! \n",
            "\n",
            "Is there anything else I can help you with?\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "That's awesome!  The 49ers are a great team.  Go Niners! \n",
            "\n",
            "Do you have a favorite player or are you excited for the upcoming season?\n"
          ]
        }
      ],
      "source": [
        "# Create a thread\n",
        "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "\n",
        "# Start conversation\n",
        "input_message = HumanMessage(content=\"hi! I'm Lance\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()\n",
        "\n",
        "input_message = HumanMessage(content=\"what's my name?\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()\n",
        "\n",
        "input_message = HumanMessage(content=\"i like the 49ers!\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "531e5b63-5e8b-486e-baa0-a45521e2fbc2",
      "metadata": {
        "id": "531e5b63-5e8b-486e-baa0-a45521e2fbc2"
      },
      "source": [
        "Now, we don't yet have a summary of the state because we still have < = 6 messages.\n",
        "\n",
        "This was set in `should_continue`.\n",
        "\n",
        "```\n",
        "    # If there are more than six messages, then we summarize the conversation\n",
        "    if len(messages) > 6:\n",
        "        return \"summarize_conversation\"\n",
        "```\n",
        "\n",
        "We can pick up the conversation because we have the thread."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "91b82aaa-17f9-49e2-9528-f4b22e23ebcb",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        },
        "id": "91b82aaa-17f9-49e2-9528-f4b22e23ebcb",
        "outputId": "a3a2a261-85d6-4af6-d556-464a82d88a52"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "''"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 19
        }
      ],
      "source": [
        "graph.get_state(config).values.get(\"summary\",\"\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "068a93e9-f716-4980-8edf-94115017d865",
      "metadata": {
        "id": "068a93e9-f716-4980-8edf-94115017d865"
      },
      "source": [
        "The `config` with thread ID allows us to proceed from the previously logged state!"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "24b34f0f-62ef-4008-8e96-480cbe92ea3e",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "24b34f0f-62ef-4008-8e96-480cbe92ea3e",
        "outputId": "6a1e5c4b-9d73-4bd6-a974-41a130d5cace"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "You're right! Nick Bosa is an absolute beast and is currently the highest-paid defensive player in the NFL.  He's a force to be reckoned with and a key part of the 49ers' success. \n",
            "\n",
            "Do you think he'll continue to dominate this season?  I'm hoping for a big year from him!\n"
          ]
        }
      ],
      "source": [
        "input_message = HumanMessage(content=\"i like Nick Bosa, isn't he the highest paid defensive player?\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "22f1b35f-e4bb-47f6-87b1-d84d8aed9aa9",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 89
        },
        "id": "22f1b35f-e4bb-47f6-87b1-d84d8aed9aa9",
        "outputId": "f57bfcba-b281-41e8-bf5c-d7c749ae7f8f"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "\"Here's a summary of our conversation:\\n\\nWe started by introducing ourselves, and you told me your name is Lance. You then shared your love for the San Francisco 49ers. We discussed your favorite player, Nick Bosa, and how he's the highest-paid defensive player in the NFL. We ended by talking about your hopes for Bosa's performance in the upcoming season. \\n\\nIt was a fun conversation about football and the 49ers! \\n\""
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 21
        }
      ],
      "source": [
        "graph.get_state(config).values.get(\"summary\",\"\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad7cc0ab-905a-4037-b7cb-69db5b89591e",
      "metadata": {
        "id": "ad7cc0ab-905a-4037-b7cb-69db5b89591e"
      },
      "source": [
        "## LangSmith\n",
        "\n",
        "Let's review the trace!"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.3"
    },
    "colab": {
      "provenance": []
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}